TSKaigi 2026
https://2026.tskaigi.org/ogp.png
deno check(型チェック)とLSPにまつわる話 実行パスと診断パスの分離
昔のdeno runは型チェックもになっていた
--no-checkでスキップ可能に
deno checkが追加とdeno runは型チェックしないのがデフォルトに
tsgoへの移行・フォーク、npmからそのまま持ってきて使えないか?というフェーズに https://gyazo.com/d04b49c474b78c1f8cc8525d425afecc
jsr:パッケージ、npm:パッケージ→TypeScriptが理解できる情報にする
実行時のコストはV8 code cacheにより最適化している(コールドスタートに近い状態)
LSPはコードの変更差分に合わせて対応する必要があって複雑性が高い denoland/typescript-goというフォークしてパッチ当てたもの
パフォーマンス
型チェックしたら約2倍のベンチマーク
https://gyazo.com/73c4d222e606f407981b37442774417d
subprocess で立ち上げ stdio IPCで繋ぐ 型チェック中にDenoを聞きにいくcallbackを足した
forkしたものを削除
公式TypeScriptに寄せる方向(純粋に使用する方法に)
deno installでtsconfigを生成する
https://gyazo.com/b87385e63f6509f62725ca03ad92c4cd
頻出例
動的境界での型落ち
DOM Event /ctach
bubblingで型が広くなってしまう
Errorインスタンス
instanceofでnarrow
型ガードを共通化してもよい
unknownでas
境界で型ガードしよう
文字列からBranded型やリテラル型の限界
型ガードやヘルパーを書く
LowerCaseなどのユーティリティ
動的な配列
Array.filter, includes, isArray
リテラル型を強制される
通常な配列にすると型が落ちる
isArrayは要素型を持たない
配列関係のヘルパーの提供
Object.fromEntries / Object.keys
型の書いてないキーもobjectと同じ型として通りうる
対応したい値を別に組み立てる
switchでfactoryのような形で対応させてセットさせる
引数のUnionから返り値を推論しづらい
引数の方に応じて帰り値の型を帰る関数をジェネリックに書けない overload関数で対応するが数が多くて大変
correlated unionsとしての改善
unionの結びつきの推論への改善はしている
分類の軸を考えてみる
境界で起きているのか・内部で起きているのか
安全圏に戻せるのか・難しいのか
どうやったらこの問題に気づける?
Flyle: VoC分析をエンタープライズ向けに展開するデータ管理プラットフォーム データベースワークフロー基盤
なぜ権限制御は複雑になりやすいか
組織構造や運用ルールで複雑化になる
フォルダ階層における権限
権限の表し方
アクション
リソースクエリ
効果
TypeScriptでの権限を一貫性整理を行う
同じ判定が複数レイヤーで出てくる
TABLE:SELECTでもそれぞれ異なる
問題を整理する
全部じゃなくて守りはどこにおくのか見え方が変わってくる
守りの根幹はDBにおくことに
最後に認可を弾くのはDBで担保
DBと同じ権限ポリシーに合わせて実装
一貫性を型で守る
DBとの値集合を型で検出する
アクションの語彙はDBにもenum型で存在することがある
語彙が二重にある
テストで検出できるようにExcludeテストで担保する
意味の異なる型をブランド型で区別
文字列ではあるが意味は異なる
string型の取り違えかた防ぐ
派生定義を受動追従
分岐漏れを検出
as const satisfiesでチェックする
権限チェックの呼び忘れはリンターで防ぐ
深さを見る
インスタンス化回数
ループ
再帰型
Union
Mapped
checker.tsの内部を見ていく
typeIDを取り出して数値として比較する二部探索
JS シングルスレッド
Go 並行モデル
型決定が非決定性をもつことになる
CompareTypes
歴史
落とし穴
構造的部分型
持っているプロパティと関数で互換性が決まる
同じ構造なら同じものとみなす
instanceofを通すと型エラーになる
プロトタイプベース
関数の呼び出しかたでthisが決まる
class機能拡張
ユースケース
ログやトレースを出す
--experimantalDecoratorsでStage 3が使える
Stage 3
Class fields
privateは完全非対応
staticの区別ができない
初期化ロジックに触れない
3からサポート
Auto-accessors
フィールド部分をgetter/setterで自明的に書く必要があった
パターン記述が簡単になった(自動生成できる)
メタデータをSymbol.metadataで読み書きできる
よくない話?
自由度が減った
prototypeやdescriptorを触ってclassの形を変更できる
直接の対象しか触れなくなった
クラスの形が静的に決まったのでエンジンの最適化に優しい
引数デコレータの未サポート
本質的には必要ではないはず
--emitDecoratorMetadata非対応
コンパイル時に型情報をメタデータを付与する機能
そもそも標準化の機能ではない
仕様やエコシステムは今どういう状況
やや厳しい。。。
テストケースと仕様の記述に矛盾が指摘されている
トランスパイラはメジャーなところはStage 3の仕様を実装済み
ブラウザエンジンは未実装
V8とSpiderMonkeyでは実装が進んでいるがまだまだ
MobX以外のライブラリはStage 3以降の対応ができていない 引数に対するデコレータはStage 1
関数と一緒に進めるべき?
一番独特な部分
AST親子で循環参照
ASTとSymbolもできる
Symbol親子も
ASTとSymbolを使って型を計算・検査
dynamic importはしんどい
内部品質が低いと機能を作りづらくなる→保守性の限界 変更したいときに変えられないのはソフトウェアの死
容易なのか困難なのかは人間が抱く感覚
予期的なもの
感覚として変更できそうと思えるもの
不安・恐怖の程度
経験的なもの
やってみての労力
影響範囲や大きさ
修正に対して閉じている
モジュールの振る舞いを変えてはいけない→閉じているべき
閉じている既存の要求を満たす振る舞いが変わらないようにする
拡張に対して開いている
モジュールには新しい振る舞いを追加できないようにする
テストは開発者の不安とりのぞく
閉鎖が保証されている
構造上の問題を教える
お前のソフトウェアはテストをかけるのか?という試金石
既存のテストに影響があるなら閉じていない
テストを書くのが大変なら開いていない